home *** CD-ROM | disk | FTP | other *** search
/ Whiteline: Alpha / Whiteline Alpha.iso / linux / atari / source / source.lzh / atari-linux-0.01pl3 / mm / mmap.c < prev    next >
Encoding:
C/C++ Source or Header  |  1994-06-08  |  12.4 KB  |  488 lines

  1. /*
  2.  *    linux/mm/mmap.c
  3.  *
  4.  * Written by obz.
  5.  *
  6.  * This file is subject to the terms and conditions of the GNU General Public
  7.  * License.  See the file README.legal in the main directory of this archive
  8.  * for more details.
  9.  */
  10. #include <linux/stat.h>
  11. #include <linux/sched.h>
  12. #include <linux/kernel.h>
  13. #include <linux/mm.h>
  14. #include <linux/shm.h>
  15. #include <linux/errno.h>
  16. #include <linux/mman.h>
  17. #include <linux/string.h>
  18. #include <linux/malloc.h>
  19.  
  20. #include <asm/segment.h>
  21. #include <asm/system.h>
  22.  
  23. static int anon_map(struct inode *, struct file *,
  24.             unsigned long, size_t, int,
  25.             unsigned long);
  26. /*
  27.  * description of effects of mapping type and prot in current implementation.
  28.  * this is due to the current handling of page faults in memory.c. the expected
  29.  * behavior is in parens:
  30.  *
  31.  * map_type    prot
  32.  *        PROT_NONE    PROT_READ    PROT_WRITE    PROT_EXEC
  33.  * MAP_SHARED    r: (no) yes     r: (yes) yes    r: (no) yes     r: (no) no
  34.  *        w: (no) yes     w: (no) copy    w: (yes) yes    w: (no) no
  35.  *        x: (no) no      x: (no) no      x: (no) no      x: (yes) no
  36.  *
  37.  * MAP_PRIVATE    r: (no) yes     r: (yes) yes    r: (no) yes     r: (no) no
  38.  *        w: (no) copy    w: (no) copy    w: (copy) copy  w: (no) no
  39.  *        x: (no) no      x: (no) no      x: (no) no      x: (yes) no
  40.  *
  41.  */
  42.  
  43. #define CODE_SPACE(addr)    \
  44.  (PAGE_ALIGN(addr) < current->start_code + current->end_code)
  45.  
  46. int do_mmap(struct file * file, unsigned long addr, unsigned long len,
  47.     unsigned long prot, unsigned long flags, unsigned long off)
  48. {
  49.     int mask, error;
  50.  
  51.     if (addr > TASK_SIZE || len > TASK_SIZE || addr > TASK_SIZE-len)
  52.         return -EINVAL;
  53.     
  54.     /*
  55.      * do simple checking here so the lower-level routines won't have
  56.      * to. we assume access permissions have been handled by the open
  57.      * of the memory object, so we don't do any here.
  58.      */
  59.     
  60.     if (file != NULL)
  61.         switch (flags & MAP_TYPE) {
  62.         case MAP_SHARED:
  63.             if ((prot & PROT_WRITE) && !(file->f_mode & 2))
  64.                 return -EACCES;
  65.             /* fall through */
  66.         case MAP_PRIVATE:
  67.             if (!(file->f_mode & 1))
  68.                 return -EACCES;
  69.             break;
  70.     
  71.         default:
  72.             return -EINVAL;
  73.         }
  74.     /*
  75.      * obtain the address to map to. we verify (or select) it and ensure
  76.      * that it represents a valid section of the address space.
  77.      */
  78.     
  79.     if (flags & MAP_FIXED) {
  80.         if (addr & ~PAGE_MASK)
  81.             return -EINVAL;
  82.         if (len > TASK_SIZE || addr > TASK_SIZE - len)
  83.             return -EINVAL;
  84.     } else {
  85.         struct vm_area_struct * vmm;
  86.  
  87.         /* Maybe this works.. Ugly it is. */
  88.         addr = SHM_RANGE_START;
  89.         while (addr+len < SHM_RANGE_END) {
  90.             for (vmm = current->mmap ; vmm ; vmm = vmm->vm_next) {
  91.                 if (addr >= vmm->vm_end)
  92.                     continue;
  93.                 if (addr + len <= vmm->vm_start)
  94.                     continue;
  95.                 addr = PAGE_ALIGN(vmm->vm_end);
  96.                 break;
  97.             }
  98.             if (!vmm)
  99.                 break;
  100.         }
  101.         if (addr+len >= SHM_RANGE_END)
  102.             return -ENOMEM;
  103.     }
  104.     
  105.     /*
  106.      * determine the object being mapped and call the appropriate
  107.      * specific mapper. the address has already been validated, but
  108.      * not unmapped, but the maps are removed from the list.
  109.      */
  110.     if (file && (!file->f_op || !file->f_op->mmap))
  111.         return -ENODEV;
  112.     mask = 0;
  113.     if (prot & (PROT_READ | PROT_EXEC))
  114.         mask |= PAGE_READONLY;
  115.     if (prot & PROT_WRITE)
  116.         if ((flags & MAP_TYPE) == MAP_PRIVATE)
  117.             mask |= PAGE_COW;
  118.         else
  119.             mask &= ~PAGE_RONLY;
  120.     if (!mask)
  121.         return -EINVAL;
  122.  
  123.     do_munmap(addr, len);    /* Clear old maps */
  124.  
  125.     if (file)
  126.         error = file->f_op->mmap(file->f_inode, file, addr, len, mask, off);
  127.     else
  128.         error = anon_map(NULL, NULL, addr, len, mask, off);
  129.     
  130.     if (!error)
  131.         return addr;
  132.  
  133.     if (!current->errno)
  134.         current->errno = -error;
  135.     return -1;
  136. }
  137.  
  138. asmlinkage int sys_mmap(unsigned long *buffer)
  139. {
  140.     int error;
  141.     unsigned long fd;
  142.     struct file * file;
  143.  
  144.     error = verify_area(VERIFY_READ, buffer, 6*4);
  145.     if (error)
  146.         return error;
  147.     fd = get_fs_long(buffer+4);
  148.     if (fd >= NR_OPEN || !(file = current->filp[fd]))
  149.         return -EBADF;
  150.     return do_mmap(file, get_fs_long(buffer), get_fs_long(buffer+1),
  151.         get_fs_long(buffer+2), get_fs_long(buffer+3), get_fs_long(buffer+5));
  152. }
  153.  
  154. /*
  155.  * Normal function to fix up a mapping
  156.  * This function is the default for when an area has no specific
  157.  * function.  This may be used as part of a more specific routine.
  158.  * This function works out what part of an area is affected and
  159.  * adjusts the mapping information.  Since the actual page
  160.  * manipulation is done in do_mmap(), none need be done here,
  161.  * though it would probably be more appropriate.
  162.  *
  163.  * By the time this function is called, the area struct has been
  164.  * removed from the process mapping list, so it needs to be
  165.  * reinserted if necessary.
  166.  *
  167.  * The 4 main cases are:
  168.  *    Unmapping the whole area
  169.  *    Unmapping from the start of the segment to a point in it
  170.  *    Unmapping from an intermediate point to the end
  171.  *    Unmapping between to intermediate points, making a hole.
  172.  *
  173.  * Case 4 involves the creation of 2 new areas, for each side of
  174.  * the hole.
  175.  */
  176. void unmap_fixup(struct vm_area_struct *area,
  177.          unsigned long addr, size_t len)
  178. {
  179.     struct vm_area_struct *mpnt;
  180.     unsigned long end = addr + len;
  181.  
  182.     if (addr < area->vm_start || addr >= area->vm_end ||
  183.         end <= area->vm_start || end > area->vm_end ||
  184.         end < addr)
  185.     {
  186.         printk("unmap_fixup: area=%lx-%lx, unmap %lx-%lx!!\n",
  187.                area->vm_start, area->vm_end, addr, end);
  188.         return;
  189.     }
  190.  
  191.     /* Unmapping the whole area */
  192.     if (addr == area->vm_start && end == area->vm_end) {
  193.         if (area->vm_ops && area->vm_ops->close)
  194.             area->vm_ops->close(area);
  195.         return;
  196.     }
  197.  
  198.     /* Work out to one of the ends */
  199.     if (addr >= area->vm_start && end == area->vm_end)
  200.         area->vm_end = addr;
  201.     if (addr == area->vm_start && end <= area->vm_end) {
  202.         area->vm_offset += (end - area->vm_start);
  203.         area->vm_start = end;
  204.     }
  205.  
  206.     /* Unmapping a hole */
  207.     if (addr > area->vm_start && end < area->vm_end)
  208.     {
  209.         /* Add end mapping -- leave beginning for below */
  210.         mpnt = (struct vm_area_struct *)kmalloc(sizeof(*mpnt), GFP_KERNEL);
  211.  
  212.         *mpnt = *area;
  213.         mpnt->vm_offset += (end - area->vm_start);
  214.         mpnt->vm_start = end;
  215.         if (mpnt->vm_inode)
  216.             mpnt->vm_inode->i_count++;
  217.         insert_vm_struct(current, mpnt);
  218.         area->vm_end = addr;    /* Truncate area */
  219.     }
  220.  
  221.     /* construct whatever mapping is needed */
  222.     mpnt = (struct vm_area_struct *)kmalloc(sizeof(*mpnt), GFP_KERNEL);
  223.     *mpnt = *area;
  224.     insert_vm_struct(current, mpnt);
  225. }
  226.  
  227.  
  228. asmlinkage int sys_mprotect(unsigned long addr, size_t len, unsigned long prot)
  229. {
  230.     return -EINVAL; /* Not implemented yet */
  231. }
  232.  
  233. asmlinkage int sys_munmap(unsigned long addr, size_t len)
  234. {
  235.     return do_munmap(addr, len);
  236. }
  237.  
  238. /*
  239.  * Munmap is split into 2 main parts -- this part which finds
  240.  * what needs doing, and the areas themselves, which do the
  241.  * work.  This now handles partial unmappings.
  242.  * Jeremy Fitzhardine <jeremy@sw.oz.au>
  243.  */
  244. int do_munmap(unsigned long addr, size_t len)
  245. {
  246.     struct vm_area_struct *mpnt, **npp, *free;
  247.  
  248.     if ((addr & ~PAGE_MASK) || addr > TASK_SIZE || len > TASK_SIZE-addr)
  249.         return -EINVAL;
  250.  
  251.     if ((len = PAGE_ALIGN(len)) == 0)
  252.         return 0;
  253.  
  254.     /*
  255.      * Check if this memory area is ok - put it on the temporary
  256.      * list if so..  The checks here are pretty simple --
  257.      * every area affected in some way (by any overlap) is put
  258.      * on the list.  If nothing is put on, nothing is affected.
  259.      */
  260.     npp = ¤t->mmap;
  261.     free = NULL;
  262.     for (mpnt = *npp; mpnt != NULL; mpnt = *npp) {
  263.         unsigned long end = addr+len;
  264.  
  265.         if ((addr < mpnt->vm_start && end <= mpnt->vm_start) ||
  266.             (addr >= mpnt->vm_end && end > mpnt->vm_end))
  267.         {
  268.             npp = &mpnt->vm_next;
  269.             continue;
  270.         }
  271.  
  272.         *npp = mpnt->vm_next;
  273.         mpnt->vm_next = free;
  274.         free = mpnt;
  275.     }
  276.  
  277.     if (free == NULL)
  278.         return 0;
  279.  
  280.     /*
  281.      * Ok - we have the memory areas we should free on the 'free' list,
  282.      * so release them, and unmap the page range..
  283.      * If the one of the segments is only being partially unmapped,
  284.      * it will put new vm_area_struct(s) into the address space.
  285.      */
  286.     while (free) {
  287.         unsigned long st, end;
  288.  
  289.         mpnt = free;
  290.         free = free->vm_next;
  291.  
  292.         st = addr < mpnt->vm_start ? mpnt->vm_start : addr;
  293.         end = addr+len;
  294.         end = end > mpnt->vm_end ? mpnt->vm_end : end;
  295.  
  296.         if (mpnt->vm_ops && mpnt->vm_ops->unmap)
  297.             mpnt->vm_ops->unmap(mpnt, st, end-st);
  298.         else
  299.             unmap_fixup(mpnt, st, end-st);
  300.  
  301.         kfree(mpnt);
  302.     }
  303.  
  304.     unmap_page_range(addr, len);
  305.     return 0;
  306. }
  307.  
  308. /* This is used for a general mmap of a disk file */
  309. int generic_mmap(struct inode * inode, struct file * file,
  310.     unsigned long addr, size_t len, int prot, unsigned long off)
  311. {
  312.       struct vm_area_struct * mpnt;
  313.     extern struct vm_operations_struct file_mmap;
  314.     struct buffer_head * bh;
  315.  
  316.     if (PAGE_IS_RW(prot)) /* only PAGE_COW or read-only supported right now */
  317.         return -EINVAL;
  318.     if (off & (inode->i_sb->s_blocksize - 1))
  319.         return -EINVAL;
  320.     if (!inode->i_sb || !S_ISREG(inode->i_mode))
  321.         return -EACCES;
  322.     if (!inode->i_op || !inode->i_op->bmap) {
  323.         return -ENOEXEC;
  324.     }
  325.     if (!(bh = bread(inode->i_dev,bmap(inode,0),inode->i_sb->s_blocksize))) {
  326.         return -EACCES;
  327.     }
  328.     /* Data cache has to be flushed here, otherwise the data and instruction
  329.      * cache would become inconsistent, resulting in execution of wrong code
  330.      * (MA)
  331.      */
  332.     cache_push (VTOP(bh->b_data), inode->i_sb->s_blocksize);
  333.  
  334.     if (!IS_RDONLY(inode)) {
  335.         inode->i_atime = CURRENT_TIME;
  336.         inode->i_dirt = 1;
  337.     }
  338.     brelse(bh);
  339.  
  340.     mpnt = (struct vm_area_struct * ) kmalloc(sizeof(struct vm_area_struct), GFP_KERNEL);
  341.     if (!mpnt)
  342.         return -ENOMEM;
  343.  
  344.     unmap_page_range(addr, len);    
  345.     mpnt->vm_task = current;
  346.     mpnt->vm_start = addr;
  347.     mpnt->vm_end = addr + len;
  348.     mpnt->vm_page_prot = prot;
  349.     mpnt->vm_share = NULL;
  350.     mpnt->vm_inode = inode;
  351.     inode->i_count++;
  352.     mpnt->vm_offset = off;
  353.     mpnt->vm_ops = &file_mmap;
  354.     insert_vm_struct(current, mpnt);
  355.     merge_segments(current->mmap, NULL, NULL);
  356.     
  357.     return 0;
  358. }
  359.  
  360. /*
  361.  * Insert vm structure into process list
  362.  * This makes sure the list is sorted by start address, and
  363.  * some some simple overlap checking.
  364.  * JSGF
  365.  */
  366. void insert_vm_struct(struct task_struct *t, struct vm_area_struct *vmp)
  367. {
  368.     struct vm_area_struct **nxtpp, *mpnt;
  369.  
  370.     nxtpp = &t->mmap;
  371.     
  372.     for(mpnt = t->mmap; mpnt != NULL; mpnt = mpnt->vm_next)
  373.     {
  374.         if (mpnt->vm_start > vmp->vm_start)
  375.             break;
  376.         nxtpp = &mpnt->vm_next;
  377.  
  378.         if ((vmp->vm_start >= mpnt->vm_start &&
  379.              vmp->vm_start < mpnt->vm_end) ||
  380.             (vmp->vm_end >= mpnt->vm_start &&
  381.              vmp->vm_end < mpnt->vm_end))
  382.             printk("insert_vm_struct: ins area %lx-%lx in area %lx-%lx\n",
  383.                    vmp->vm_start, vmp->vm_end,
  384.                    mpnt->vm_start, vmp->vm_end);
  385.     }
  386.     
  387.     vmp->vm_next = mpnt;
  388.  
  389.     *nxtpp = vmp;
  390. }
  391.  
  392. /*
  393.  * Merge a list of memory segments if possible.
  394.  * Redundant vm_area_structs are freed.
  395.  * This assumes that the list is ordered by address.
  396.  */
  397. void merge_segments(struct vm_area_struct *mpnt,
  398.             map_mergep_fnp mergep, void *mpd)
  399. {
  400.     struct vm_area_struct *prev, *next;
  401.  
  402.     if (mpnt == NULL)
  403.         return;
  404.     
  405.     for(prev = mpnt, mpnt = mpnt->vm_next;
  406.         mpnt != NULL;
  407.         prev = mpnt, mpnt = next)
  408.     {
  409.         int mp;
  410.  
  411.         next = mpnt->vm_next;
  412.         
  413.         if (mergep == NULL)
  414.         {
  415.             unsigned long psz = prev->vm_end - prev->vm_start;
  416.             mp = prev->vm_offset + psz == mpnt->vm_offset;
  417.         }
  418.         else
  419.             mp = (*mergep)(prev, mpnt, mpd);
  420.  
  421.         /*
  422.          * Check they are compatible.
  423.          * and the like...
  424.          * What does the share pointer mean?
  425.          */
  426.         if (prev->vm_ops != mpnt->vm_ops ||
  427.             prev->vm_page_prot != mpnt->vm_page_prot ||
  428.             prev->vm_inode != mpnt->vm_inode ||
  429.             prev->vm_end != mpnt->vm_start ||
  430.             !mp ||
  431.             prev->vm_share != mpnt->vm_share ||        /* ?? */
  432.             prev->vm_next != mpnt)            /* !!! */
  433.             continue;
  434.  
  435.         /*
  436.          * merge prev with mpnt and set up pointers so the new
  437.          * big segment can possibly merge with the next one.
  438.          * The old unused mpnt is freed.
  439.          */
  440.         prev->vm_end = mpnt->vm_end;
  441.         prev->vm_next = mpnt->vm_next;
  442.         kfree_s(mpnt, sizeof(*mpnt));
  443.         mpnt = prev;
  444.     }
  445. }
  446.  
  447. /*
  448.  * Map memory not associated with any file into a process
  449.  * address space.  Adjecent memory is merged.
  450.  */
  451. static int anon_map(struct inode *ino, struct file * file,
  452.             unsigned long addr, size_t len, int mask,
  453.             unsigned long off)
  454. {
  455.       struct vm_area_struct * mpnt;
  456.  
  457.     if (zeromap_page_range(addr, len, mask))
  458.         return -ENOMEM;
  459.  
  460.     mpnt = (struct vm_area_struct * ) kmalloc(sizeof(struct vm_area_struct), GFP_KERNEL);
  461.     if (!mpnt)
  462.         return -ENOMEM;
  463.  
  464.     mpnt->vm_task = current;
  465.     mpnt->vm_start = addr;
  466.     mpnt->vm_end = addr + len;
  467.     mpnt->vm_page_prot = mask;
  468.     mpnt->vm_share = NULL;
  469.     mpnt->vm_inode = NULL;
  470.     mpnt->vm_offset = 0;
  471.     mpnt->vm_ops = NULL;
  472.     insert_vm_struct(current, mpnt);
  473.     merge_segments(current->mmap, ignoff_mergep, NULL);
  474.  
  475.     return 0;
  476. }
  477.  
  478. /* Merge, ignoring offsets */
  479. int ignoff_mergep(const struct vm_area_struct *m1,
  480.           const struct vm_area_struct *m2,
  481.           void *data)
  482. {
  483.     if (m1->vm_inode != m2->vm_inode)    /* Just to be sure */
  484.         return 0;
  485.  
  486.     return (struct inode *)data == m1->vm_inode;
  487. }
  488.